// lib/base/http.ts
import { API_CONFIG } from "../api/config";
import type { ApiResponse } from "../types";
import { HttpError, NetworkError, TimeoutError } from "./error";

/**
 * HTTP 请求配置接口，继承自 RequestInit
 * @interface RequestOptions
 * @extends {RequestInit}
 */
export interface RequestOptions extends RequestInit {
  /** URL 查询参数 */
  params?: Record<string, string | number>;
  /** 请求超时时间（毫秒） */
  timeout?: number;
  /** 是否需要携带认证信息 */
  withAuth?: boolean;
}

/**
 * API 响应数据接口
 * @interface ApiResponseData
 */
export interface ApiResponseData {
  /** 响应消息 */
  message?: string;
  [key: string]: unknown;
}

/**
 * HTTP 客户端类
 * 实现单例模式的 HTTP 请求封装
 */
class HttpClient {
  private static instance: HttpClient;
  private baseUrl: string;
  private defaultTimeout: number;

  private constructor() {
    this.baseUrl = API_CONFIG.baseUrl;
    this.defaultTimeout = API_CONFIG.timeout;
  }

  /**
   * 获取 HttpClient 单例实例
   * @returns {HttpClient} HttpClient 实例
   */
  static getInstance(): HttpClient {
    if (!HttpClient.instance) {
      HttpClient.instance = new HttpClient();
    }
    return HttpClient.instance;
  }

  /**
   * 获取认证令牌
   * @returns {string | null} 认证令牌或 null
   * @private
   */
  private getAuthToken(): string | null {
    if (typeof window !== "undefined") {
      return localStorage.getItem("auth_token");
    }
    return null;
  }

  /**
   * 创建完整的请求 URL
   * @param {string} endpoint - API 端点
   * @param {Record<string, string | number>} [params] - URL 查询参数
   * @returns {string} 完整的请求 URL
   * @private
   */
  private createUrl(
    endpoint: string,
    params?: Record<string, string | number>
  ): string {
    const url = new URL(endpoint, this.baseUrl);
    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        url.searchParams.append(key, String(value));
      });
    }
    return url.toString();
  }

  /**
   * 带超时的请求方法
   * @template T - 响应数据类型
   * @param {string} url - 请求 URL
   * @param {RequestOptions} options - 请求配置
   * @returns {Promise<T>} 请求响应
   * @private
   * @throws {HttpError} HTTP 错误
   * @throws {TimeoutError} 超时错误
   * @throws {NetworkError} 网络错误
   */
  private async fetchWithTimeout<T>(
    url: string,
    options: RequestOptions
  ): Promise<T> {
    const {
      timeout = this.defaultTimeout,
      withAuth = false,
      ...init
    } = options;

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);

    try {
      const headers = new Headers({
        "Content-Type": "application/json",
        ...init.headers,
      });

      if (withAuth) {
        const token = this.getAuthToken();
        if (token) {
          headers.set("Authorization", `Bearer ${token}`);
        }
      }

      const response = await fetch(url, {
        ...init,
        headers,
        signal: controller.signal,
      });

      let data: ApiResponseData;
      try {
        data = await response.json();
      } catch {
        throw new NetworkError("响应解析失败");
      }

      if (!response.ok) {
        throw new HttpError(response.status, data.message || "请求失败", data);
      }

      // 如果是登录接口，直接返回响应数据
      if (url.includes('/login')) {
        return data as T;
      }

      // 其他接口，包装成 ApiResponse 格式
      return {
        code: response.status,
        data: data as T,
        message: data.message || "请求成功"
      } as T;
    } catch (error) {
      if (error instanceof HttpError) {
        throw error;
      }
      if (error instanceof Error && error.name === "AbortError") {
        throw new TimeoutError();
      }
      if (error instanceof NetworkError) {
        throw error;
      }
      throw new NetworkError("网络请求失败");
    } finally {
      clearTimeout(timeoutId);
    }
  }

  /**
   * 发送 GET 请求
   * @template T - 响应数据类型
   * @param {string} endpoint - API 端点
   * @param {RequestOptions} [options] - 请求配置
   * @returns {Promise<ApiResponse<T>>} API 响应
   */
  async get<T = unknown>(
    endpoint: string,
    options: RequestOptions = {}
  ): Promise<ApiResponse<T>> {
    const url = this.createUrl(endpoint, options.params);
    return this.fetchWithTimeout<ApiResponse<T>>(url, {
      ...options,
      method: "GET",
    });
  }

  /**
   * 发送 POST 请求
   * @template T - 响应数据类型
   * @param {string} endpoint - API 端点
   * @param {unknown} [data] - 请求数据
   * @param {RequestOptions} [options] - 请求配置
   * @returns {Promise<T>} API 响应
   */
  async post<T = unknown>(
    endpoint: string,
    data?: unknown,
    options: RequestOptions = {}
  ): Promise<T> {
    const url = this.createUrl(endpoint);
    return this.fetchWithTimeout<T>(url, {
      ...options,
      method: "POST",
      body: data ? JSON.stringify(data) : undefined,
    });
  }

  /**
   * 发送 PUT 请求
   * @template T - 响应数据类型
   * @param {string} endpoint - API 端点
   * @param {unknown} [data] - 请求数据
   * @param {RequestOptions} [options] - 请求配置
   * @returns {Promise<ApiResponse<T>>} API 响应
   */
  async put<T = unknown>(
    endpoint: string,
    data?: unknown,
    options: RequestOptions = {}
  ): Promise<ApiResponse<T>> {
    const url = this.createUrl(endpoint);
    return this.fetchWithTimeout<ApiResponse<T>>(url, {
      ...options,
      method: "PUT",
      body: data ? JSON.stringify(data) : undefined,
    });
  }

  /**
   * 发送 DELETE 请求
   * @template T - 响应数据类型
   * @param {string} endpoint - API 端点
   * @param {RequestOptions} [options] - 请求配置
   * @returns {Promise<ApiResponse<T>>} API 响应
   */
  async delete<T = unknown>(
    endpoint: string,
    options: RequestOptions = {}
  ): Promise<ApiResponse<T>> {
    const url = this.createUrl(endpoint);
    return this.fetchWithTimeout<ApiResponse<T>>(url, {
      ...options,
      method: "DELETE",
    });
  }
}

/** 导出 HttpClient 单例实例 */
export const http = HttpClient.getInstance();
